package com.p7.architect.store.shoppingcart.api.web.controller;

import com.p7.architect.store.shoppingcart.api.base.Proto;
import com.p7.architect.store.shoppingcart.api.base.Result;
import com.p7.architect.store.shoppingcart.api.web.converter.ShoppingCartVoMapper;
import com.p7.architect.store.shoppingcart.api.web.request.AddItemsRequest;
import com.p7.architect.store.shoppingcart.api.web.request.FindItemsRequest;
import com.p7.architect.store.shoppingcart.api.web.request.RemoveItemsRequest;
import com.p7.architect.store.shoppingcart.api.web.request.UpdateItemsRequest;
import com.p7.architect.store.shoppingcart.api.web.response.ShoppingCartResponse;
import com.p7.architect.store.shoppingcart.application.command.AddItemCommand;
import com.p7.architect.store.shoppingcart.application.command.RemoveItemsCommand;
import com.p7.architect.store.shoppingcart.application.command.UpdateItemsCommand;
import com.p7.architect.store.shoppingcart.application.command.dto.ShoppingCartDto;
import com.p7.architect.store.shoppingcart.application.command.executor.AddItemCommandService;
import com.p7.architect.store.shoppingcart.application.command.executor.RemoveItemsCommandService;
import com.p7.architect.store.shoppingcart.application.command.executor.UpdateItemsCommandService;
import com.p7.architect.store.shoppingcart.application.query.ShoppingCartInfoQuery;
import com.p7.architect.store.shoppingcart.application.query.ShoppingCartInfoQueryService;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ItemNum;
import com.p7.architect.store.shoppingcart.domain.model.shoppingcart.ShoppingCartId;
import com.p7.architect.store.shoppingcart.domain.model.value.SkuId;
import com.p7.architect.store.shoppingcart.domain.model.value.StoreId;
import com.p7.architect.store.shoppingcart.domain.model.value.UserId;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RestController
@RequestMapping(value = "/shoppingCart")
@RequiredArgsConstructor
public class ShoppingCartController {

    private final AddItemCommandService addItemCommandService;

    private final ShoppingCartInfoQueryService shoppingCartInfoQueryService;

    private final UpdateItemsCommandService updateItemsCommandService;

    private final RemoveItemsCommandService removeItemsCommandService;

    @PostMapping(value = "/addItem")
    public Result<ShoppingCartResponse> addItem(@Valid @RequestBody AddItemsRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return Result.error(new Proto(Proto.PARAMETER_IS_EMPTY_OR_ERROR.getCode(),
                    Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()));
        }

        try {
            ShoppingCartId shoppingCartId = addItemCommandService.execute(
                    new AddItemCommand(
                            new SkuId(request.getSkuId()),
                            new ItemNum(request.getCount()),
                            new UserId(request.getUserId()),
                            new StoreId(request.getStoreId())));

            return getShoppingCartResponseResult(shoppingCartId);
        } catch (Exception e) {
            return Result.error(Proto.SYSTEM_ERROR);
        }
    }

    @PostMapping(value = "/updateItems")
    public Result<ShoppingCartResponse> updateItems(@Valid @RequestBody UpdateItemsRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return Result.error(new Proto(Proto.PARAMETER_IS_EMPTY_OR_ERROR.getCode(),
                    Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()));
        }

        try {
            ShoppingCartId shoppingCartId = updateItemsCommandService.execute(
                    new UpdateItemsCommand(
                            new ShoppingCartId(request.getShoppingCartId()),
                            new SkuId(request.getSkuId()),
                            new ItemNum(request.getCount())));
            if (null == shoppingCartId) {
                return Result.success(null);
            }

            return getShoppingCartResponseResult(shoppingCartId);
        } catch (Exception e) {
            return Result.error(Proto.SYSTEM_ERROR);
        }
    }

    @PostMapping(value = "/removeItems")
    public Result<ShoppingCartResponse> removeItems(@Valid @RequestBody RemoveItemsRequest request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return Result.error(new Proto(Proto.PARAMETER_IS_EMPTY_OR_ERROR.getCode(),
                    Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()));
        }

        List<SkuId> skuIdList = request.getSkuIdList().stream().map(SkuId::new).collect(Collectors.toList());
        try {
            ShoppingCartId shoppingCartId = removeItemsCommandService.execute(
                    new RemoveItemsCommand(
                            new ShoppingCartId(request.getShoppingCartId()),
                            skuIdList));
            if (null == shoppingCartId) {
                return Result.success(null);
            }

            return getShoppingCartResponseResult(shoppingCartId);
        } catch (Exception e) {
            return Result.error(Proto.SYSTEM_ERROR);
        }
    }

    @GetMapping(value = "/findItems")
    public Result<ShoppingCartResponse> findItems(@ModelAttribute FindItemsRequest request) {
        ShoppingCartDto shoppingCartDTO = shoppingCartInfoQueryService.queryWithUserIdAndStoreId(
                new ShoppingCartInfoQuery(new UserId(request.getUserId()), new StoreId(request.getStoreId())));

        ShoppingCartResponse shoppingCartResponse = null;
        if (null != shoppingCartDTO) {
            shoppingCartResponse = ShoppingCartVoMapper.INSTANCE.toShoppingCartResponse(shoppingCartDTO);
        }

        return Result.success(shoppingCartResponse);
    }

    @NotNull
    private Result<ShoppingCartResponse> getShoppingCartResponseResult(ShoppingCartId shoppingCartId) {
        ShoppingCartDto shoppingCartDTO = shoppingCartInfoQueryService.queryWithCartId(new ShoppingCartInfoQuery(shoppingCartId));

        ShoppingCartResponse shoppingCartResponse = null;
        if (null != shoppingCartDTO) {
            shoppingCartResponse = ShoppingCartVoMapper.INSTANCE.toShoppingCartResponse(shoppingCartDTO);
        }

        return Result.success(shoppingCartResponse);
    }
}